/********************************************************************************/
/*                                                                              */
/*              JiveRunTime.java                                                */
/*                                                                              */
/*      Run time entries for JIVE visualization                                 */
/*                                                                              */
/********************************************************************************/
/*      Copyright 2002 Brown University -- Steven P. Reiss                    */
/*********************************************************************************
 *  Copyright 2002, Brown University, Providence, RI.                            *
 *                                                                               *
 *                        All Rights Reserved                                    *
 *                                                                               *
 *  Permission to use, copy, modify, and distribute this software and its        *
 *  documentation for any purpose other than its incorporation into a            *
 *  commercial product is hereby granted without fee, provided that the          *
 *  above copyright notice appear in all copies and that both that               *
 *  copyright notice and this permission notice appear in supporting             *
 *  documentation, and that the name of Brown University not be used in          *
 *  advertising or publicity pertaining to distribution of the software          *
 *  without specific, written prior permission.                                  *
 *                                                                               *
 *  BROWN UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS                *
 *  SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND            *
 *  FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL BROWN UNIVERSITY      *
 *  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY          *
 *  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,              *
 *  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS               *
 *  ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE          *
 *  OF THIS SOFTWARE.                                                            *
 *                                                                               *
 ********************************************************************************/

/* RCS: $Header: /pro/spr_cvs/pro/bloom/javasrc/edu/brown/bloom/jive/JiveRunTime.java,v 1.29 2007-05-04 20:37:23 spr Exp $ */


/*********************************************************************************
 *
 * $Log: JiveRunTime.java,v $
 * Revision 1.29  2007-05-04 20:37:23  spr
 * Formatting changes; fix up org.* libraries; bettern run time initializatios.
 *
 * Revision 1.28  2007-01-03 03:30:54  spr
 * Formating changes.
 *
 * Revision 1.27  2006/07/03 18:17:08  spr
 * Clean up code.
 *
 * Revision 1.26  2006/02/21 17:12:24  spr
 * Add EXIT state; fixup error messages.
 *
 * Revision 1.25  2005/09/02 15:09:38  spr
 * Remove unneeded color code; fix scaling; change lock ids.
 *
 * Revision 1.24  2005/07/08 21:05:46  spr
 * Better track of timings.
 *
 * Revision 1.23  2005/06/07 02:21:48  spr
 * Changes for java 5.0.
 *
 * Revision 1.22  2005/05/10 17:17:54  spr
 * Updates for java 5.0.
 *
 * Revision 1.21  2005/05/08 03:12:36  spr
 * Fix bad transcript file; fix Apple AWT thread name.
 *
 * Revision 1.20  2005/05/07 22:35:56  spr
 * Updates for java 5.0.        Add phase control panel.
 *
 * Revision 1.19  2005/01/15 02:14:53  spr
 * Remove the need to create a thread class.  Prepare for java 1.5
 *
 * Revision 1.18  2004/11/24 22:57:13  spr
 * Improve run time by avoiding memory allocation.
 *
 * Revision 1.17  2004/05/28 21:05:37  spr
 * Change csh to tcsh.
 *
 * Revision 1.16  2004/03/26 16:35:16  spr
 * Add horizontal (row) display for threads; compute blocks after the fact
 * rather than at run time; fix output for change information.
 *
 * Revision 1.15  2004/03/05 03:27:13  spr
 * Add a local counter to make up for lack of a high-res timer.
 *
 * Revision 1.14  2004/01/16 18:16:12  spr
 * Add hooks for thread information (when we have a detailed timer); add save
 * transcript option.
 *
 * Revision 1.13  2003/12/17 21:25:14  spr
 * Enable production of thread synchronization and state change events.
 *
 * Revision 1.12  2003/08/14 19:13:11  spr
 * Formatting changes.
 *
 * Revision 1.11  2003/07/16 20:01:19  spr
 * Minor bug fixes in jive as seen in demos; add mkmovie to cvs.
 *
 * Revision 1.10  2003/06/09 14:50:07  spr
 * Handle mac internal threads as waiting.
 *
 * Revision 1.9  2003/05/30 18:11:36  spr
 * Add properties for jive-specific items for binary distribution; remove
 * support for MINT.
 *
 * Revision 1.8  2003/05/24 00:25:01  spr
 * Buffer files to improve performance.
 *
 * Revision 1.7  2003/05/16 21:01:48  spr
 * Bug fixes from demo; ensure we can detail the library classes.
 *
 * Revision 1.6  2003/04/24 20:08:37  spr
 * Handle native methods that are triggers by using a stub; handle AWT-Motif thread.
 *
 * Revision 1.5  2003/02/06 20:40:11  spr
 * Fix box display overwriting lines; add lock-on statistics for threads.
 *
 * Revision 1.4  2002/11/15 20:38:59  spr
 * Fix problems with multiple runs.  Add brightness and tie to synchronizations.
 *
 * Revision 1.3  2002/11/01 21:02:17  spr
 * Complete the jive implementation with output for threads and classes.
 *
 * Revision 1.2  2002/10/26 14:56:29  spr
 * Output setup information listing classes at startup.
 *
 * Revision 1.1  2002/10/24 20:22:07  spr
 * Initial version of the JIVE java interactive viewing tool.  This version only
 * includes the code to patch and generate periodic information about a running
 * program.
 *
 *
 ********************************************************************************/


package edu.brown.bloom.jive;


import org.w3c.dom.*;
import javax.xml.parsers.*;


import java.util.*;
import java.io.*;
import java.net.*;
import java.awt.*;



public class JiveRunTime implements JiveRunTimeConstants
{




/********************************************************************************/
/*                                                                              */
/*      Local Storage                                                           */
/*                                                                              */
/********************************************************************************/

private static JiveRunTime              the_runtime = null;
private static Object                   runtime_mutex = null;

private ItemTable       item_table;

private int             thread_counter;

private JiveTimer       jive_timer;

private int []          index_map;
private int []          code_map;
private int             base_index;

private ThreadState []  thread_table;

private Stats           current_stats;
private Stats           eval_stats;
private Stats           next_stats;

private Client          out_client;
private XmlWriter       xml_out;
private String          process_id;

private Map<String,RtClass> class_map;

private boolean         is_apple;

private volatile long   local_time;



private static final int        SAVE_DEPTH = 64;

private static final int        GC_MAX = 100;           // max gc per interval

private static final int        EVENT_MAX = 512;        // set to 0 for no events
                                                        // set to 256-1024 for events
                                                        // events need a better timer




/********************************************************************************/
/*                                                                              */
/*      Top level entry points                                                  */
/*                                                                              */
/********************************************************************************/

public static void handleStart()
{
   if (the_runtime != null) return;

   the_runtime = new JiveRunTime();
}



public static void handleEnter(int mid)
{
   if (the_runtime != null) the_runtime.processEnter(mid);
}


public static void handleExit(int mid)
{
   if (the_runtime != null) the_runtime.processExit(mid);
}



public static void handleAlloc(int mid,int cid)
{
   if (the_runtime != null) the_runtime.processAlloc(mid,cid);
}




public static void handleLock(Object mon)
{
   if (the_runtime != null) the_runtime.processLock(mon,0); }



public static void handleLockEnter(Object mon)
{
   if (the_runtime != null) the_runtime.processLock(mon,1);
}



public static void handleLockExit(Object mon)
{
   if (the_runtime != null) the_runtime.processLock(mon,2);
}



public static void handleFree(Object obj)
{
// System.err.println("FREE");
}




/********************************************************************************/
/*                                                                              */
/*      Constructors                                                            */
/*                                                                              */
/********************************************************************************/

JiveRunTime()
{
   String sockid = null;

   String xmlfile = System.getProperty(JIVE_PROPERTY_XML_FILE);
   process_id = System.getProperty(JIVE_PROPERTY_PID);
   if (process_id == null) process_id = "ANY";
   String vendor = System.getProperty("java.vendor");
   is_apple = vendor.startsWith("Apple");

   sockid = System.getProperty(JIVE_PROPERTY_SOCKET_ID);

   item_table = new ItemTable();
   index_map = null;
   code_map = null;
   class_map = new HashMap<String,RtClass>();
   thread_counter = 0;
   thread_table = new ThreadState[JIVE_MAX_THREADS];
   for (int i = 0; i < JIVE_MAX_THREADS; ++i) thread_table[i] = null;

   try {
      FileInputStream fr = new FileInputStream(xmlfile);
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      dbf.setValidating(false);
      DocumentBuilder db = dbf.newDocumentBuilder();
      Document d = db.parse(fr);
      fr.close();
      Node n = d.getDocumentElement();
      setupXmlData(n);
      item_table.setup();
      loadXmlData(n);
    }
   catch (Exception e) {
      System.err.println("JIVE: Problem parsing xml file: " + e.getMessage());
      e.printStackTrace();
    }

   setupIndexMap();
   current_stats = new Stats();
   eval_stats = new Stats();
   next_stats = new Stats();

   local_time = 0;
   current_stats.setTime(System.currentTimeMillis(),local_time);

   if (sockid != null) {
      out_client = new SocketClient(sockid);
    }
   else {
      out_client = new FileClient();
    }
   xml_out = out_client.getXmlWriter();

   outputSetup();

   System.getProperties().remove(JIVE_PROPERTY_SOCKET_ID);

   jive_timer = new JiveTimer();
   jive_timer.start();
}



/********************************************************************************/
/*                                                                              */
/*      Processing methods                                                      */
/*                                                                              */
/********************************************************************************/

private void processEnter(int mid)
{
   int idx = index_map[mid-base_index];
   if (idx != 0) {
      current_stats.item_counts[idx]++;
    }

   local_time++;

   int code = code_map[mid-base_index];

   if (code != 0) {
      switch (code) {
         case JIVE_CODE_NORMAL :
            break;
         case JIVE_CODE_WAIT :
            setState(JIVE_STATE_WAIT,null);
            break;
         case JIVE_CODE_SLEEP :
            setState(JIVE_STATE_SLEEP,null);
            break;
         case JIVE_CODE_IO :
            setState(JIVE_STATE_IO,null);
            break;
         case JIVE_CODE_THREAD :
            setState(JIVE_STATE_RUN,null);
            break;
         case JIVE_CODE_FREE :
            break;
         case JIVE_CODE_EXIT :
            setState(JIVE_STATE_NEWRUN,null);
            break;
         case JIVE_CODE_CHECK :
            break;
       }
    }
}



private void processExit(int mid)
{
   int code = code_map[mid-base_index];

   local_time++;

   if (code != 0) {
      switch (code) {
         case JIVE_CODE_NORMAL :
            break;
         case JIVE_CODE_WAIT :
         case JIVE_CODE_SLEEP :
         case JIVE_CODE_IO :
         case JIVE_CODE_EXIT :
            setState(JIVE_STATE_REVERT,null);
            break;
         case JIVE_CODE_THREAD :
            setState(JIVE_STATE_DEAD,null);
            break;
         case JIVE_CODE_FREE :
            break;
         case JIVE_CODE_CHECK :
            // System.err.println("CHECK EXIT " + Thread.currentThread());
            break;
       }
    }

}



private void processAlloc(int mid,int cid)
{
   local_time++;

   int idx = index_map[mid-base_index];
   if (idx != 0) {
      current_stats.alloc_by[idx]++;
    }

   if (cid != 0) {
      idx = index_map[cid-base_index];
      if (idx != 0) {
         current_stats.alloc_of[idx]++;
       }
    }
}



private void processLock(Object o,int state)
{
   switch (state) {
      case 0 :                  // lock
         setState(JIVE_STATE_BLOCK,o);
         if (o != null) {
            String cnm = o.getClass().getName();
            RtClass rc = class_map.get(cnm);
            if (rc != null) {
               int idx = index_map[rc.getIndex()-base_index];
               if (idx > 0) current_stats.block_count[idx]++;
             }
          }
         break;
      case 1 :
         setState(JIVE_STATE_SYNC,o);
         break;
      case 2 :
         setState(JIVE_STATE_REVERT,o);
         break;
    }
}




/********************************************************************************/
/*                                                                              */
/*      Thread state management methods                                         */
/*                                                                              */
/********************************************************************************/

private void setState(int state,Object o)
{
   Thread t = Thread.currentThread();
   ThreadState ts = null;
   int thidx = -1;

   // System.err.println("SET STATE " + state + " " + t.getName());

   if (t == jive_timer) return;

   ts = getThreadState(t);

   ts.setState(state,o);
}



private ThreadState getThreadState(Thread t)
{
   ThreadState ts = null;
   int thidx = -1;

   if (t == jive_timer) return null;

   for (int i = 0; i <= thread_counter; ++i) {
      ThreadState its = thread_table[i];
      if (its != null && its.getThread() == t) {
         thidx = i;
         ts = its;
         break;
       }
    }
   if (ts == null) {
      synchronized(this) {
         thidx = ++thread_counter;
         // t.__jive_field__ = thidx;
       }
      ts = new ThreadState(thidx,t);
      thread_table[thidx] = ts;
      checkStackTrace(ts);
    }

   return ts;
}



private void checkStackTrace(ThreadState ts)
{
   if (ts.getCurrentState() != JIVE_STATE_RUN) return;
   StackTraceElement [] stk = Thread.currentThread().getStackTrace();
   for (int i = 0; i < stk.length; ++i) {
      if (stk[i].getMethodName().equals("run") &&
             stk[i].getClassName().equals("java.lang.Thread")) {
         for (int j = i-1; j >= 0; --j) {
            String nm = stk[j].getClassName() + "." + stk[j].getMethodName();
            RtMethod mthd = item_table.setThreadMethod(nm);
            if (mthd != null) {
               code_map[mthd.getIndex()-base_index] = mthd.getCode();
               ts.setExitMethod(mthd);
               break;
             }
          }
       }
    }
}




private void checkThreads()
{
   for (int i = 0; i <= thread_counter; ++i) {
      ThreadState ts = thread_table[i];
      if (ts != null) ts.check();
    }
}




private class ThreadState {

   private Thread for_thread;
   private int current_state;
   private long start_time;
   private int thread_index;
   private Object lock_object;
   private int [] prior_state;
   private Object [] prior_object;
   private int prior_ct;
   private int change_ct;
   private int base_state;
   private RtMethod exit_method;

   ThreadState(int idx,Thread t) {
      for_thread = t;
      if (t.getName().equals("AWT-Motif")) current_state = JIVE_STATE_IO;
      else if (t.getName().equals("AWT-Shutdown")) current_state = JIVE_STATE_WAIT;
      else if (t.getName().equals("Reference Handler")) current_state = JIVE_STATE_WAIT;
      else if (t.getName().equals("Finalizer")) current_state = JIVE_STATE_WAIT;
      else if (t.getName().startsWith("AWT-EventQueue")) current_state = JIVE_STATE_IO;
      else if (t.getName().startsWith("Timer-")) current_state = JIVE_STATE_WAIT;
      else if (is_apple && t.getName().startsWith("Thread-")) current_state = JIVE_STATE_WAIT;
      else if (t.getName().endsWith("Timer")) current_state = JIVE_STATE_WAIT;
      else current_state = JIVE_STATE_RUN;
      base_state = current_state;
      prior_state = new int[SAVE_DEPTH];
      prior_object = new Object[SAVE_DEPTH];
      prior_ct = 0;
      change_ct = 0;
      pushState();
      start_time = System.currentTimeMillis();
      thread_index = idx;
      lock_object = null;
      if (current_state == JIVE_STATE_WAIT) lock_object = t;
      exit_method = null;
    }

   Thread getThread()                           { return for_thread; }

   int getCurrentState()                        { return current_state; }

   void setExitMethod(RtMethod m)               { exit_method = m; }

   void setState(int state,Object o) {
      boolean lockchange = false;
      ++change_ct;
      if (state == JIVE_STATE_WAIT && lock_object == null) {
         if (o != null) lock_object = o;
         else lock_object = for_thread;
         for (int i = prior_ct-1; i >= 0; --i) {
            if (prior_state[i] == JIVE_STATE_RUN || prior_state[i] == JIVE_STATE_NEW) {
               prior_state[i] = JIVE_STATE_SYNC;
             }
            prior_object[i] = lock_object;
          }
       }

      switch (state) {
         case JIVE_STATE_BLOCK :
         case JIVE_STATE_IO :
         case JIVE_STATE_SLEEP :
         case JIVE_STATE_WAIT :
         case JIVE_STATE_GC :
         case JIVE_STATE_NEWRUN :
            pushState();
            break;
         case JIVE_STATE_REVERT :
            if (o != null && o == lock_object) lockchange = true;
            o = null;
            state = popState();
            lock_object = topObject();
            break;
       }

      int blockon = 0;
      if (state == JIVE_STATE_BLOCK && o != null && EVENT_MAX == 0) {
         for (int i = 1; i < thread_counter; ++i) {
            ThreadState ts = thread_table[i];
            if (ts == null || ts == this) continue;
            boolean fg = false;
            if (ts.lock_object == o && ts.current_state == JIVE_STATE_SYNC) {
               fg = true;
             }
            else if (ts.current_state != JIVE_STATE_WAIT) {
               for (int j = 0; j < ts.prior_ct; ++j) {
                  if (ts.prior_object[j] == o && ts.prior_state[j] == JIVE_STATE_SYNC) {
                     fg = true;
                     break;
                   }
                }
             }
            if (fg) {
               blockon = i;
               current_stats.addThreadLock(i);
               break;
             }
          }
       }

      if (state == JIVE_STATE_NEWRUN) state = JIVE_STATE_RUN;
      if (state == JIVE_STATE_RUN) {
         if (lock_object != null) state = JIVE_STATE_SYNC;
         o = null;
       }
      if (state == JIVE_STATE_BLOCK) lock_object = o;

      long time = System.currentTimeMillis();
      long ltime = getLocalTime();

//    if (state == current_state) return;
      if (state == current_state && !lockchange) return;

      current_stats.setThread(thread_index,current_state,start_time,time,ltime,state,blockon,lock_object);
      current_state = state;
      start_time = time;
    }

   void outputFields() {
      xml_out.field("INDEX",thread_index);
      xml_out.field("NAME",for_thread.getName());
    }

   boolean isSynced() {
      if (lock_object == null) return false;
      return true;
    }

   private void pushState() {
      prior_object[prior_ct] = lock_object;
      prior_state[prior_ct++] = current_state;
      if (prior_ct >= SAVE_DEPTH) --prior_ct;
    }
   private int popState() {
      if (prior_ct > 0) --prior_ct;
      return prior_state[prior_ct];
    }
   private Object topObject() {
      return prior_object[prior_ct];
    }

   void check() {
      if (for_thread != null && current_state != JIVE_STATE_DEAD && !for_thread.isAlive()) {
         setState(JIVE_STATE_DEAD,null);
       }
    }

}       // end of ThreadState




/********************************************************************************/
/*                                                                              */
/*      Statistics output methods                                               */
/*                                                                              */
/********************************************************************************/

private void outputSetup()
{
   out_client.sendHeader();

   xml_out.begin("JIVERUN");
   xml_out.field("PID",process_id);
   xml_out.begin("DATA");
   xml_out.field("TIME",System.currentTimeMillis());
   for (int i = 1; i < index_map.length; ++i) {
      if (index_map[i] == i) {
         RtItem ri = item_table.getItem(i+base_index);
         if (ri != null) ri.outputItem(xml_out);
       }
    }
   xml_out.end();
   xml_out.end();

   out_client.sendTrailer();
}




private void collectStatistics()
{
   long time  = System.currentTimeMillis();
   long ltime = getLocalTime();

   next_stats.reset();

   Stats x = eval_stats;
   eval_stats = current_stats;
   current_stats = next_stats;
   next_stats = x;
   current_stats.setTime(time,ltime);

   out_client.sendHeader();

   xml_out.begin("JIVERUN");
   xml_out.field("PID",process_id);
   xml_out.begin("STATS");
   xml_out.field("TIME",time);
   xml_out.field("LOCALTIME",ltime);

   int tot0 = 0;
   int tot1 = 0;
   int tot2 = 0;
   int tot3 = 0;
   for (int i = 0; i < eval_stats.item_counts.length; ++i) {
      int i0 = eval_stats.item_counts[i];
      int i1 = eval_stats.alloc_of[i];
      int i2 = eval_stats.alloc_by[i];
      int i3 = eval_stats.block_count[i];

      if (i0 + i1 + i2 + i3 == 0) continue;

      xml_out.begin("ENTRY");
      RtItem ri = item_table.getItem(i+base_index);
      xml_out.field("NAME",ri.getName());
      if (i0 != 0) xml_out.field("COUNT",i0);
      if (i1 != 0) xml_out.field("AOF",i1);
      if (i2 != 0) xml_out.field("ABY",i2);
      if (i3 != 0) xml_out.field("BLOCK",i3);
      xml_out.end();

      tot0 += i0;
      tot1 += i1;
      tot2 += i2;
      tot3 += i3;
    }

   xml_out.begin("TOTALS");
   xml_out.field("COUNT",tot0);
   xml_out.field("AOF",tot1);
   xml_out.field("ABY",tot2);
   if (tot3 != 0) xml_out.field("BLOCK",tot3);
   xml_out.end();

   eval_stats.fixThreads(next_stats,time,ltime);
   eval_stats.outputThreads();

   xml_out.end();
   xml_out.end();

   out_client.sendTrailer();
}




/********************************************************************************/
/*                                                                              */
/*      Methods to load XML data from patcher                                   */
/*                                                                              */
/********************************************************************************/

private void setupXmlData(Node xml)
{
   if (xml.getNodeName().equals("JIVE")) {
      for (Node n = xml.getFirstChild(); n != null; n = n.getNextSibling()) {
         if (n.getNodeType() == Node.ELEMENT_NODE) setupXmlData(n);
       }
    }
   else if (xml.getNodeName().equals("METHOD")) {
      setupItem(xml);
    }
   else if (xml.getNodeName().equals("CLASS")) {
      setupItem(xml);
    }
   else if (xml.getNodeName().equals("PACKAGE")) {
      setupItem(xml);
    }
}



private void setupItem(Node xml)
{
   int idx = getAttrInt(xml,"INDEX");
   item_table.noteIndex(idx);
}




private void loadXmlData(Node xml)
{
   if (xml.getNodeName().equals("JIVE")) {
      for (Node n = xml.getFirstChild(); n != null; n = n.getNextSibling()) {
         if (n.getNodeType() == Node.ELEMENT_NODE) loadXmlData(n);
       }
    }
   else if (xml.getNodeName().equals("METHOD")) {
      loadMethod(xml);
    }
   else if (xml.getNodeName().equals("CLASS")) {
      loadClass(xml);
    }
   else if (xml.getNodeName().equals("PACKAGE")) {
      loadPackage(xml);
    }
}


private void loadMethod(Node xml)
{
   String nm = getAttrString(xml,"NAME");
   String sign = getAttrString(xml,"SIGNATURE");
   int sln = getAttrInt(xml,"START");
   int eln = getAttrInt(xml,"END");
   int idx = getAttrInt(xml,"INDEX");
   String file = getAttrString(xml,"FILE");
   int code = getAttrInt(xml,"CODE");
   int map = getAttrInt(xml,"MAP");

   RtMethod rm = new RtMethod(idx,nm,sign,sln,eln,file,code,map);
   item_table.setElement(idx,rm);
}



private void loadClass(Node xml)
{
   String nm = getAttrString(xml,"NAME");
   int idx = getAttrInt(xml,"INDEX");
   String fl = getAttrString(xml,"FILE");
   int map = getAttrInt(xml,"MAP");
   RtClass rc = new RtClass(idx,nm,fl,map);
   item_table.setElement(idx,rc);
   class_map.put(nm,rc);
}




private void loadPackage(Node xml)
{
   String nm = getAttrString(xml,"NAME");
   int idx = getAttrInt(xml,"INDEX");
   RtPackage rp = new RtPackage(idx,nm);
   item_table.setElement(idx,rp);
}




private String getAttrString(Node xml,String id)
{
   if (xml != null && xml instanceof Element) {
      Element e = (Element) xml;
      return e.getAttribute(id);
    }

   return null;
}



private int getAttrInt(Node xml,String id)
{
   if (xml != null && xml instanceof Element) {
      Element e = (Element) xml;
      String s = e.getAttribute(id);
      try {
         return Integer.parseInt(s);
       }
      catch (NumberFormatException ex) { }
    }

   return -1;
}




/********************************************************************************/
/*                                                                              */
/*      Methods to take care of mapping                                         */
/*                                                                              */
/********************************************************************************/

private void setupIndexMap()
{
   int bid = item_table.getBaseId();
   int mid = item_table.getMaxId();

   index_map = new int[mid-bid+2];
   code_map = new int[mid-bid+2];
   base_index = bid-1;

   index_map[0] = 0;

   for (int i = bid; i <= mid; ++i) {
      RtMethod rm = item_table.getMethod(i);
      if (rm != null) {
         code_map[i-base_index] = rm.getCode();
         index_map[i-base_index] = rm.getMap()-base_index;
       }
      else {
         code_map[i-base_index] = JIVE_CODE_NORMAL;
         RtClass rc = item_table.getClass(i);
         if (rc != null) index_map[i-base_index] = rc.getMap()-base_index;
         else index_map[i-base_index] = 0;
       }
      int j = index_map[i-base_index];
      if (j <= 0) index_map[i-base_index] = 0;
      else if (index_map[j] == 0) index_map[j] = j;
    }
}




/********************************************************************************/
/*                                                                              */
/*      Local time counter for high-resolution timing                           */
/*                                                                              */
/********************************************************************************/

private long getLocalTime()
{
   return local_time++;                 // this is purposely not synchronized
                                        // but should give approximate relative times
                                        // for events
}


/********************************************************************************/
/*                                                                              */
/*      Methods to handle JVMTI interface                                       */
/*                                                                              */
/********************************************************************************/

private static int native_ok = -1;

private int getGCInfo(long when,long [] start,long [] end,Thread [] thrd)
{
   if (native_ok == 0) return 0;

   try {
      int sz = nativeGetGCInfo(when,start,end,thrd);
      if (sz > 0) {
         for (int i = 0; i < sz; ++i) {
            ThreadState ts = getThreadState(thrd[0]);
          }
       }
      native_ok = 1;
      return sz;
    }
   catch (Throwable t) {
//    System.err.println("JVMTI ERROR: " + t);
      native_ok = 0;
    }

   return 0;
}



private native static int nativeGetGCInfo(long when,long [] start,long [] end,Thread [] thrd);




/********************************************************************************/
/*                                                                              */
/*      Class to hold the index table                                           */
/*                                                                              */
/********************************************************************************/

private static class ItemTable {

   private RtItem [] the_table;
   private int base_id;
   private int max_id;

   ItemTable() {
      base_id = -1;
      max_id = 0;
      the_table = null;
    }

   void noteIndex(int idx) {
      if (base_id < 0 || idx < base_id) base_id = idx;
      if (idx > max_id) max_id = idx;
    }

   void setup() {
      the_table = new RtItem[max_id-base_id+1];
      for (int i = 0; i < the_table.length; ++i) the_table[i] = null;
    }

   void setElement(int idx,RtItem itm)          { the_table[idx-base_id] = itm; }

   RtItem getItem(int idx) {
      return the_table[idx-base_id];
    }

   RtMethod getMethod(int idx) {
      RtItem itm = getItem(idx);
      if (itm != null && itm instanceof RtMethod) return (RtMethod) itm;
      return null;
    }

   RtClass getClass(int idx) {
      RtItem itm = getItem(idx);
      if (itm != null && itm instanceof RtClass) return (RtClass) itm;
      return null;
    }

   RtPackage getPackage(int idx) {
      RtItem itm = getItem(idx);
      if (itm != null && itm instanceof RtPackage) return (RtPackage) itm;
      return null;
    }

   int getBaseId()                              { return base_id; }
   int getMaxId()                               { return max_id; }

   RtMethod setThreadMethod(String nm) {
      for (int i = 0; i < the_table.length; ++i) {
         if (the_table[i] != null) {
            if (nm.equals(the_table[i].getName())) {
               RtMethod m = the_table[i].setThreadMethod();
               if (m != null) return m;
             }
          }
       }
      return null;
    }

}




/********************************************************************************/
/*                                                                              */
/*      Top level class for method or block                                     */
/*                                                                              */
/********************************************************************************/

private abstract static class RtItem {

   private int item_index;
   private String item_name;

   protected RtItem(int idx,String nm) {
      item_index = idx;
      item_name = nm;
    }

   int getIndex()                               { return item_index; }
   String getName()                             { return item_name; }

   abstract void outputItem(XmlWriter xw);
   RtMethod setThreadMethod()                   { return null; }

}       // end of subclass RtItem



/********************************************************************************/
/*                                                                              */
/*      Class representing a method                                             */
/*                                                                              */
/********************************************************************************/

private static class RtMethod extends RtItem {

   private String method_sign;
   private int start_line;
   private int end_line;
   private String method_file;
   private int method_code;
   private int method_map;

   RtMethod(int idx,String nm,String sg,int sln,int eln,String fil,int cd,int map) {
      super(idx,nm);
      method_sign = sg;
      start_line = sln;
      end_line = eln;
      method_file = fil;
      method_code = cd;
      method_map = map;
    }

   int getMap()                                 { return method_map; }
   int getCode()                                { return method_code; }

   RtMethod setThreadMethod() {
      if (method_code == JIVE_CODE_NORMAL) {
         method_code = JIVE_CODE_CHECK;
         return this;
       }
      else if (method_code == JIVE_CODE_CHECK) return this;
      else if (method_code == JIVE_CODE_THREAD) return this;
      return null;
    }

   void outputItem(XmlWriter xw) {
      xw.begin("METHOD");
      xw.field("NAME",getName());
      if (start_line > 0) {
         xw.field("START",start_line);
         xw.field("END",end_line);
       }
      if (method_file != null) xw.field("FILE",method_file);
      xw.end();
    }

}       // end of subclass RtMethod





/********************************************************************************/
/*                                                                              */
/*      Class representing a class                                              */
/*                                                                              */
/********************************************************************************/

private static class RtClass extends RtItem {

   private String class_file;
   private int class_map;

   RtClass(int idx,String nm,String fl,int mp) {
      super(idx,nm);
      class_file = fl;
      class_map = mp;
    }

   int getMap()                                 { return class_map; }

   void outputItem(XmlWriter xw) {
      xw.begin("CLASS");
      xw.field("NAME",getName());
      xw.end();
    }

}       // end of subclass RtClass





/********************************************************************************/
/*                                                                              */
/*      Class representing a package                                            */
/*                                                                              */
/********************************************************************************/

private static class RtPackage extends RtItem {

   RtPackage(int idx,String nm) {
      super(idx,nm);
    }

   void outputItem(XmlWriter xw) {
      xw.begin("PACKAGE");
      xw.field("NAME",getName());
      xw.end();
    }

}       // end of subclass RtPackage





/********************************************************************************/
/*                                                                              */
/*      Class for statistical data                                              */
/*                                                                              */
/********************************************************************************/

private class Stats {

   int [] item_counts;
   int [] alloc_of;
   int [] alloc_by;
   int [] block_count;
   ThreadStats [] thread_data;
   long stat_time;
   long interval_time;
   long local_time;
   long local_interval;
   long [] gc_start;
   long [] gc_end;
   Thread [] gc_thread;
   int gc_ctr;

   Stats() {
      int bid = item_table.getBaseId();
      int mid = item_table.getMaxId();

      item_counts = new int [mid-bid+1];
      alloc_of = new int [mid-bid+1];
      alloc_by = new int [mid-bid+1];
      block_count = new int [mid-bid+1];
      thread_data = new ThreadStats[64];
      gc_start = new long[GC_MAX];
      gc_end = new long[GC_MAX];
      gc_thread = new Thread[GC_MAX];
      gc_ctr = 0;
      for (int i = 0; i < thread_data.length; ++i) thread_data[i] = new ThreadStats();
      reset();
      stat_time = 0;
    }

   void setTime(long t,long lt) {
      stat_time = t;
      local_time = lt;
    }

   void reset() {
      int ln = item_counts.length;
      for (int i = 0; i < ln; ++i) {
         item_counts[i] = 0;
         alloc_of[i] = 0;
         alloc_by[i] = 0;
         block_count[i] = 0;
       }
      int mxt = thread_counter+4;
      if (thread_data.length < mxt) {
         int nln = mxt*2;
         ThreadStats [] od = thread_data;
         thread_data = new ThreadStats[nln];
         for (int i = 0; i < od.length; ++i) thread_data[i] = od[i];
         for (int i = od.length; i < thread_data.length; ++i) thread_data[i] = new ThreadStats();
       }
      for (int i = 1; i < mxt; ++i) {
         ThreadState th = thread_table[i];
         if (th == null) break;
         thread_data[i].reset();
       }
      gc_ctr = 0;
    }

   void setThread(int idx,int state,long start,long time,long ltime,int nstate,int blkon,Object lck) {
      if (idx >= thread_data.length) return;
      if (start < stat_time) start = stat_time;
      thread_data[idx].set(state,time-start,time,nstate);
      thread_data[idx].stateChange(nstate,time,ltime,blkon,lck);
    }

   void addThreadLock(int idx) {
      if (idx >= thread_data.length) return;
      thread_data[idx].addBlock();
    }

   void fixThreads(Stats prior,long until,long luntil) {
      gc_ctr = getGCInfo(until,gc_start,gc_end,gc_thread);
      for (int i = 1; i < thread_data.length; ++i) {
         ThreadStats ts = thread_data[i];
         if (ts.last_state == JIVE_STATE_NEW) break;
         if (ts.last_state == JIVE_STATE_STABLE && i < prior.thread_data.length) {
            ts.updateFrom(prior.thread_data[i],i);
          }
         if (i < prior.thread_data.length) ts.start_state = prior.thread_data[i].last_state;
         ts.finishInterval(stat_time,until);
         for (int j = 0; j < gc_ctr; ++j) {
            if (gc_thread[j] == thread_table[i].getThread()) {
               ts.mergeGCInfo(j,gc_ctr,gc_start,gc_end,gc_thread);
               break;
             }
          }
         interval_time = until-stat_time;
         local_interval = luntil-local_time;
       }
    }

   void outputThreads() {
      double [] totals = new double[JIVE_STATE_MAX];
      int blocks = 0;
      for (int i = 1; i < thread_data.length; ++i) {
         ThreadStats ts = thread_data[i];
         if (ts.last_state == JIVE_STATE_NEW) break;
         ts.updateTotals(totals);
         blocks += ts.getBlocks();
         xml_out.begin("THREAD");
         thread_table[i].outputFields();
         ts.output();
         xml_out.end();
       }
      xml_out.begin("THTOTAL");
      xml_out.field("ITIME",interval_time);
      xml_out.field("LITIME",local_interval);
      for (int i = 0; i < JIVE_STATE_MAX; ++i) {
         if (totals[i] != 0) {
            xml_out.field(state_names[i],(long) totals[i]);
          }
       }
      if (blocks != 0) xml_out.field("BLOCKS",blocks);
      xml_out.end();
    }

}       // end of subclass Stats




private class ThreadStats {

   double [] state_times;
   long last_time;
   int last_state;
   int block_ons;
   private int start_state;
   private long [] change_time;
   private long [] change_ltime;
   private int [] change_id;
   private Object [] change_lock;
   private int change_ctr;
   private long [] gc_start;
   private long [] gc_end;
   private int gc_ctr;

   ThreadStats() {
      state_times = new double[JIVE_STATE_MAX];
      last_time = 0;
      last_state = JIVE_STATE_NEW;
      start_state = JIVE_STATE_NEW;
      block_ons = 0;
      change_time = new long [EVENT_MAX];
      change_ltime = new long [EVENT_MAX];
      change_id = new int [EVENT_MAX];
      change_lock = new Object [EVENT_MAX];
      gc_start = new long[GC_MAX];
      gc_end = new long[GC_MAX];
      change_ctr = 0;
      gc_ctr = 0;
    }

   void reset() {
      for (int i = 0; i < JIVE_STATE_MAX; ++i) state_times[i] = 0;
      last_state = JIVE_STATE_STABLE;
      last_time = 0;
      block_ons = 0;
      change_ctr = 0;
      gc_ctr = 0;
    }

   void set(int state,long delta,long time,int nstate) {
      if (delta == 0) state_times[state] += JIVE_ZERO_TIME;
      else state_times[state] += delta;
      last_time = time;
      last_state = nstate;
    }

   void finishInterval(long start,long until) {
      if (last_time > start) start = last_time;
      if (last_state >= 0) state_times[last_state] += until-start;
    }

   void output() {
      for (int i = 0; i < JIVE_STATE_MAX; ++i) {
         if (state_times[i] != 0) {
            xml_out.field(state_names[i],(long) state_times[i]);
          }
       }
      if (block_ons != 0) xml_out.field("BLOCKS",block_ons);
      xml_out.field("LAST",last_state);
      xml_out.field("START",start_state);
      if (change_ctr > 0) {
         xml_out.begin("CHANGES");
         long t0 = change_time[0];
         long t1 = change_ltime[0];
         xml_out.field("TIME",t0);
         xml_out.field("LTIME",t1);
         for (int i = 0; i < change_ctr; ++i) {
            xml_out.begin("AT");
            xml_out.field("TIME",change_time[i]-t0);
            xml_out.field("LTIME",change_ltime[i]-t1);
            int st = change_id[i] % JIVE_STATE_MAX;
            int blk = change_id[i] / JIVE_STATE_MAX;
            xml_out.field("STATE",state_names[st]);
            if (change_lock[i] != null) xml_out.field("LOCK",
                                                         System.identityHashCode(change_lock[i]));
            if (blk > 0) xml_out.field("BLOCK",blk);
            xml_out.end();
          }
         xml_out.end();
       }
      else if (gc_ctr > 0) {
         xml_out.begin("CHANGES");
         long t0 = gc_start[0]/1000000;
         xml_out.field("TIME",(long) t0);
         for (int i = 0; i < gc_ctr; ++i) {
            long ts = gc_start[i]/1000000;
            long te = gc_end[i]/1000000;
            xml_out.begin("AT");
            xml_out.field("TIME",ts - t0);
            xml_out.field("STATE","GC");
            xml_out.field("TONLY",true);
            xml_out.end();
            xml_out.begin("AT");
            xml_out.field("TIME",te - t0);
            xml_out.field("STATE",state_names[last_state]);
            xml_out.field("TONLY",true);
            xml_out.end();
          }
         xml_out.end();
       }
    }

   void updateTotals(double [] totals) {
      for (int i = 0; i < JIVE_STATE_MAX; ++i) {
         totals[i] += state_times[i];
       }
    }

   void updateFrom(ThreadStats ps,int idx) {
      last_time = ps.last_time;
      if (ps.last_state != JIVE_STATE_NEW) last_state = ps.last_state;
      else if (idx < thread_table.length && thread_table[idx] != null) {
         ThreadState ts = thread_table[idx];
         last_state = ts.getCurrentState();
       }
      else if (thread_table[idx].isSynced()) last_state = JIVE_STATE_SYNC;
      else last_state = JIVE_STATE_RUN;
      start_state = last_state;
    }

   void addBlock()                              { ++block_ons; }
   int getBlocks()                              { return block_ons; }

   void stateChange(int state,long time,long ltime,int blk,Object lck) {
      if (change_ctr >= EVENT_MAX) return;
      if (state == JIVE_STATE_SYNC && change_ctr > 0 &&
             (change_id[change_ctr-1] % JIVE_STATE_MAX) == JIVE_STATE_BLOCK &&
             change_ltime[change_ctr-1] == ltime-1)
         --change_ctr;
      change_time[change_ctr] = time;
      change_ltime[change_ctr] = ltime;
      change_id[change_ctr] = state + blk*JIVE_STATE_MAX;
      change_lock[change_ctr] = lck;
      ++change_ctr;
    }

   void mergeGCInfo(int me,int ctr,long [] start,long [] end,Thread [] thrd) {
      Thread t = thrd[me];
      for (int i = 0; i < ctr; ++i) {
         if (thrd[i] == t) {
            gc_start[gc_ctr] = start[i];
            gc_end[gc_ctr] = end[i];
            ++gc_ctr;
            long d = (end[i] - start[i])/1000000;
            for (int j = 0; j < JIVE_STATE_MAX; ++j) {
               if (state_times[j] > d && j != JIVE_STATE_GC) {
                  state_times[j] -= d;
                  state_times[JIVE_STATE_GC] += d;
                  break;
                }
             }
          }
       }
    }


}       // end of subclass ThreadStats





/********************************************************************************/
/*                                                                              */
/*      Timing trigger class                                                    */
/*                                                                              */
/********************************************************************************/

private class JiveTimer extends Thread
{
   private long time_delay;

   JiveTimer() {
      super("JiveTimer");

      setPriority(Thread.MAX_PRIORITY);

      String td = System.getProperty(JIVE_PROPERTY_INTERVAL);
      if (td == null) time_delay = JIVE_TIME_DELAY;
      else {
         try {
            time_delay = Integer.parseInt(td);
          }
         catch (NumberFormatException e) {
            time_delay = JIVE_TIME_DELAY;
          }
       }
      setDaemon(true);
    }

   public void run() {
      for (int i = 0; ; ++i) {
         try {
            try {
               sleep(time_delay);
               setPriority(Thread.MAX_PRIORITY);
             }
            catch (InterruptedException e) {
               break;
             }
            if ((i % 10) == 0) checkThreads();
            collectStatistics();
          }
         catch (Throwable t) {
            System.err.println("JIVE: COLLECT aborted: " + t);
            t.printStackTrace();
          }
       }
    }

}       // end of subclass JiveTimer




/********************************************************************************/
/*                                                                              */
/*      XmlWriterClass                                                          */
/*                                                                              */
/********************************************************************************/


private static class XmlWriter extends PrintWriter
{
   private Writer          base_writer;
   private Vector<String>  element_stack;
   private int             open_state;
   private String          indent_string;

   private static final int        STATE_DONE = 0;
   private static final int        STATE_OPEN = 1;
   private static final int        STATE_CLOSED = 2;
   private static final int        STATE_TEXT = 3;


   XmlWriter(Writer w) {
      super(w);
      base_writer = w;
      element_stack = new Vector<String>();
      indent_string = "  ";
      indent_string = null;             // save space
      open_state = STATE_DONE;
    }

   void begin(String elt) {
      switch (open_state) {
         case STATE_DONE :
            break;
         case STATE_OPEN :
            println(">");
            break;
         case STATE_CLOSED :
            break;
       }
      indent();
      print("<");
      print(elt);
      open_state = STATE_OPEN;
      element_stack.addElement(elt);
    }

   void end() {
      int ln = element_stack.size();
      if (ln == 0) throw new Error("End with no corresponding begin");
      String elt = element_stack.lastElement();
      element_stack.setSize(ln-1);

      if (open_state == STATE_DONE) return;
      else if (open_state == STATE_OPEN) {
         println(" />");
       }
      else if (elt != null) {
         if (open_state != STATE_TEXT) indent();
         print("</");
         print(elt);
         println(">");
       }
      if (ln == 1) open_state = STATE_DONE;
      else open_state = STATE_CLOSED;
    }

   void field(String elt,String val) {
      if (open_state != STATE_OPEN) throw new Error("Field must be specified inside an element");
      print(" ");
      print(elt);
      print("='");
      outputXmlString(val);
      print("'");
    }

   void field(String elt,boolean fg)                    { field(elt,String.valueOf(fg)); }
   void field(String elt,short v)                       { field(elt,String.valueOf(v)); }
   void field(String elt,int v)                         { field(elt,String.valueOf(v)); }
   void field(String elt,long v)                        { field(elt,Long.toString(v)); }
   void field(String elt,double v)                      { field(elt,String.valueOf(v)); }

   void text(String t) {
      if (open_state == STATE_OPEN) {
         print(">");
         open_state = STATE_TEXT;
       }
      outputXmlString(t);
    }

   private void indent() {
      if (indent_string != null) {
         int ln = element_stack.size();
         for (int i = 0; i < ln; ++i) print(indent_string);
       }
    }

   private void outputXmlString(String s) {
      if (s == null) return;
      for (int i = 0; i < s.length(); ++i) {
         char c = s.charAt(i);
         switch (c) {
            case '&' :
               print("&amp;");
               break;
            case '<' :
               print("&lt;");
               break;
            case '>' :
               print("&gt;");
               break;
            case '"' :
               print("&quot;");
               break;
            case '\'' :
               print("&apos;");
               break;
            default :
               print(c);
               break;
          }
       }
    }

}       // end of subclass XmlWriter




/********************************************************************************/
/*                                                                              */
/*      Class to set up a connection to JIVE                                    */
/*                                                                              */
/********************************************************************************/

private abstract static class Client {

   abstract void sendHeader();

   abstract void sendTrailer();

   abstract XmlWriter getXmlWriter();

}       // end of abstract class Client




private static class FileClient extends Client {

   XmlWriter xml_writer;

   FileClient() {
      try {
         xml_writer = new XmlWriter(new BufferedWriter(new FileWriter("trace.out"),1024*1024));
       }
      catch (IOException e) {
         System.err.println("JIVE: Problem setting up trace file: " + e.getMessage());
         System.exit(1);
       }
    }

   void sendHeader()                            { }
   void sendTrailer()                           { xml_writer.flush(); }
   XmlWriter getXmlWriter()                     { return xml_writer; }

}       // end of abstract class FileClient



private static class SocketClient extends Client {

   private StringWriter string_writer;
   private OutputStream output_stream;
   private OutputStreamWriter output_writer;
   private char [] char_buf;
   private char [] char_trailer;
   private byte [] byte_buffer;

   SocketClient(String loc) {
      output_stream = null;
      char_buf = new char[65536];
      String eom = JIVE_TRAILER + "\n";
      byte_buffer = new byte[65536];
      char_trailer = new char[eom.length()];
      eom.getChars(0,eom.length(),char_trailer,0);
      while (output_stream == null) {
         try {
            StringTokenizer tok = new StringTokenizer(loc,"\t@");
            String host = tok.nextToken().trim();
            int port = Integer.parseInt(tok.nextToken());
            Socket cs = new Socket(host,port);
            output_stream = cs.getOutputStream();
            string_writer = new StringWriter(65536);
          }
         catch (Exception e) {
            System.err.println("JIVE: Waiting for server connection: " + e.getMessage());
          }
         if (output_stream == null) {
            try {
               Thread.currentThread().sleep(1);
             }
            catch (InterruptedException e) { }
          }
       }
    }

   void sendHeader() {
    }

   void sendTrailer() {
      StringBuffer sbuf = string_writer.getBuffer();
      int slen = sbuf.length();
      if (char_buf.length < slen) char_buf = new char[slen*2];
      sbuf.getChars(0,slen,char_buf,0);
      sbuf.delete(0,slen);
      if (slen + char_trailer.length > byte_buffer.length) {
         byte_buffer = new byte[slen*2 + char_trailer.length];
       }
      for (int i = 0; i < slen; ++i) {
         byte_buffer[i] = (byte) char_buf[i];
       }
      for (int i = 0; i < char_trailer.length; ++i) {
         byte_buffer[i+slen] = (byte) char_trailer[i];
       }
      try {
         output_stream.write(byte_buffer,0,slen+char_trailer.length);
         output_stream.flush();
       }
      catch (IOException e) { }
    }

   XmlWriter getXmlWriter()             { return new XmlWriter(string_writer); }

}       // end of subclass SocketClient




}       // end of class JiveRunTime




/* end of JiveRunTime.java */

